package com.sh.weblogspringboot.admin.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.sh.weblogspringboot.admin.convert.ArticleDetailConvert;
import com.sh.weblogspringboot.admin.service.AdminArticleService;
import com.sh.weblogspringboot.admin.vo.article.DeleteArticleReqVO;
import com.sh.weblogspringboot.admin.vo.article.FindArticleDetailReqVO;
import com.sh.weblogspringboot.admin.vo.article.FindArticleDetailRspVO;
import com.sh.weblogspringboot.admin.vo.article.FindArticlePageListReqVO;
import com.sh.weblogspringboot.admin.vo.article.FindArticlePageListRspVO;
import com.sh.weblogspringboot.admin.vo.article.PublishArticleReqVO;
import com.sh.weblogspringboot.admin.vo.article.UpdateArticleReqVO;
import com.sh.weblogspringboot.common.domain.dos.ArticleCategoryRelDO;
import com.sh.weblogspringboot.common.domain.dos.ArticleContentDO;
import com.sh.weblogspringboot.common.domain.dos.ArticleDO;
import com.sh.weblogspringboot.common.domain.dos.ArticleTagRelDO;
import com.sh.weblogspringboot.common.domain.dos.CategoryDO;
import com.sh.weblogspringboot.common.domain.dos.TagDO;
import com.sh.weblogspringboot.common.domain.mapper.ArticleCategoryRelMapper;
import com.sh.weblogspringboot.common.domain.mapper.ArticleContentMapper;
import com.sh.weblogspringboot.common.domain.mapper.ArticleMapper;
import com.sh.weblogspringboot.common.domain.mapper.ArticleTagRelMapper;
import com.sh.weblogspringboot.common.domain.mapper.CategoryMapper;
import com.sh.weblogspringboot.common.domain.mapper.TagMapper;
import com.sh.weblogspringboot.common.enums.ResponseCodeEnum;
import com.sh.weblogspringboot.common.exception.BizException;
import com.sh.weblogspringboot.common.util.PageResponse;
import com.sh.weblogspringboot.common.util.Response;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author songHai
 * @Classname AdminArticleServiceImpl
 * @Description
 * @Date 2025/4/8 23:10
 */
@Service
@Slf4j
public class AdminArticleServiceImpl implements AdminArticleService {

    @Autowired
    private ArticleMapper articleMapper;
    @Autowired
    private ArticleContentMapper articleContentMapper;
    @Autowired
    private ArticleCategoryRelMapper articleCategoryRelMapper;
    @Autowired
    private CategoryMapper categoryMapper;
    @Autowired
    private ArticleTagRelMapper articleTagRelMapper;
    @Autowired
    private TagMapper tagMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response publishArticle(PublishArticleReqVO publishArticleReqVO) {
        // 1. VO 转Articledo 保存
        ArticleDO articleDO = ArticleDO.builder().title(publishArticleReqVO.getTitle()).cover(publishArticleReqVO.getCover()).summary(publishArticleReqVO.getSummary()).createTime(LocalDateTime.now()).updateTime(LocalDateTime.now()).build();
        articleMapper.insert(articleDO);

        // 获取插入保存的文章主键ID
        Long articleId = articleDO.getId();

        // 2. VO 转ArticleContentDO 保存
        ArticleContentDO articleContentDO = ArticleContentDO.builder().articleId(articleId).content(publishArticleReqVO.getContent()).build();
        articleContentMapper.insert(articleContentDO);

        // 3. 处理文章关联的分类
        Long categoryId = publishArticleReqVO.getCategoryId();
        // 3.1 校验提交的分类是否存在
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDO)) {
            log.warn("==>分类不存在， categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }

        // 3.2 分类存在保存文章分类关系表
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder().articleId(articleId).categoryId(categoryId).build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);

        // 4. 保存文章关联标签集合
        List<String> publishTags = publishArticleReqVO.getTags();
        insertTags(articleId, publishTags);
        return Response.success("发布文章成功");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteArticle(DeleteArticleReqVO deleteArticleReqVO) {
        Long articleId = deleteArticleReqVO.getId();

        // 删除文章
        articleMapper.deleteById(articleId);

        // 删除文章内容
        articleContentMapper.deleteByArticleId(articleId);

        // 删除文章-分类关联记录
        articleCategoryRelMapper.deleteByArticleId(articleId);

        // 删除文章-标签关联记录
        articleTagRelMapper.deleteByArticleId(articleId);
        return Response.success("删除文章成功");
    }

    @Override
    public Response findArticlePageList(FindArticlePageListReqVO findArticlePageListReqVO) {
        // 获取当前页、以及每页需要展示的数据数量
        Long current = findArticlePageListReqVO.getCurrent();
        Long size = findArticlePageListReqVO.getSize();
        String title = findArticlePageListReqVO.getTitle();
        LocalDate startDate = findArticlePageListReqVO.getStartDate();
        LocalDate endDate = findArticlePageListReqVO.getEndDate();

        // 执行分页查询
        Page<ArticleDO> articleDOPage = articleMapper.selectPageList(current, size, title, startDate, endDate);

        List<ArticleDO> articleDOS = articleDOPage.getRecords();
        // DO 转 VO
        List<FindArticlePageListRspVO> vos = null;
        if (!CollectionUtils.isEmpty(articleDOS)) {
            vos = articleDOS.stream().map(articleDO -> FindArticlePageListRspVO.builder()
                            .id(articleDO.getId())
                            .title(articleDO.getTitle())
                            .cover(articleDO.getCover())
                            .createTime(articleDO.getCreateTime())
                            .build())
                    .collect(Collectors.toList());
        }

        return PageResponse.success(articleDOPage, vos);
    }

    @Override
    public Response findArticleDetail(FindArticleDetailReqVO findArticleDetailReqVO) {
        Long articleId = findArticleDetailReqVO.getId();

        ArticleDO articleDO = articleMapper.selectById(articleId);

        if (Objects.isNull(articleDO)) {
            log.warn("==> 查询的文章不存在，articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        ArticleContentDO articleContentDO = articleContentMapper.selectByArticleId(articleId);

        // 所属分类
        ArticleCategoryRelDO articleCategoryRelDO = articleCategoryRelMapper.selectByArticleId(articleId);

        // 对应标签
        List<ArticleTagRelDO> articleTagRelDOS = articleTagRelMapper.selectByArticleId(articleId);
        // 获取对应标签 ID 集合
        List<Long> tagIds = articleTagRelDOS.stream().map(ArticleTagRelDO::getTagId).collect(Collectors.toList());

        // DO 2 VO
        FindArticleDetailRspVO vo = ArticleDetailConvert.INSTANCE.convertDO2VO(articleDO);
        vo.setContent(articleContentDO.getContent());
        vo.setCategoryId(articleCategoryRelDO.getCategoryId());
        vo.setTagIds(tagIds);

        return Response.success("查询成功", vo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateArticle(@NotNull UpdateArticleReqVO updateArticleReqVO) {
        Long articleId = updateArticleReqVO.getId();

        // 1. VO 转 ArticleDO, 并更新
        ArticleDO articleDO = ArticleDO.builder()
                .id(articleId)
                .title(updateArticleReqVO.getTitle())
                .cover(updateArticleReqVO.getCover())
                .summary(updateArticleReqVO.getSummary())
                .updateTime(LocalDateTime.now())
                .build();

        int count = articleMapper.updateById(articleDO);
        // 根据更新是否成功，来判断该文章是否存在
        if (count == 0) {
            log.warn("==> 该文章不存在, articleId: {}", articleId);
            throw new BizException(ResponseCodeEnum.ARTICLE_NOT_FOUND);
        }

        // 2. VO 转 ArticleContentDO，并更新
        ArticleContentDO articleContentDO = ArticleContentDO.builder()
                .articleId(articleId)
                .content(updateArticleReqVO.getContent())
                .build();
        articleContentMapper.updateByArticleId(articleContentDO);

        // 3. 更新文章分类
        Long categoryId = updateArticleReqVO.getCategoryId();
        // 3.1 校验提交的分类是否真实存在
        CategoryDO categoryDO = categoryMapper.selectById(categoryId);
        if (Objects.isNull(categoryDO)) {
            log.warn("==> 分类不存在, categoryId: {}", categoryId);
            throw new BizException(ResponseCodeEnum.CATEGORY_NOT_EXISTED);
        }
        // 先删除该文章关联的分类记录，再插入新的关联关系
        articleCategoryRelMapper.deleteByArticleId(articleId);
        ArticleCategoryRelDO articleCategoryRelDO = ArticleCategoryRelDO.builder()
                .articleId(articleId)
                .categoryId(categoryId)
                .build();
        articleCategoryRelMapper.insert(articleCategoryRelDO);

        // 4. 保存文章关联的标签集合
        // 先删除该文章对应的标签
        articleTagRelMapper.deleteByArticleId(articleId);
        List<String> publishTags = updateArticleReqVO.getTags();
        insertTags(articleId, publishTags);

        return Response.success("更新成功");
    }

    /**
     * 批量保存文章关联的标签集合.
     *
     * @param publishTags
     */
    private void insertTags(Long articleId, List<String> publishTags) {
        // 筛选提交的标签（表中不存在的标签）
        List<String> notExistTags = null;
        // 筛选提交的标签（表中已存在的标签）
        List<String> existedTags = null;

        // 查询出所有标签(TODO 全表扫描存在慢SQL)
        List<TagDO> tagDOList = tagMapper.selectList(null);

        // 如果表中还没有添加任何标签
        if (CollectionUtils.isEmpty(tagDOList)) {
            notExistTags = publishTags;
        } else {
            List<String> tagIds = tagDOList.stream().map(tagDO -> String.valueOf(tagDO.getId())).collect(Collectors.toList());
            // 表中已添加相关标签，需要筛选
            // 通过标签ID来筛选，包含对应ID则表示提交的标签已存在表中
            existedTags = publishTags.stream().filter(tagIds::contains).collect(Collectors.toList());
            // 否则则死不存在的
            notExistTags = publishTags.stream().filter(publishTag -> !tagIds.contains(publishTag)).collect(Collectors.toList());

            // 还有中可能：桉字符串名称提交上来的标签，有可能死表中包含的，比如表中已有Java标签，用户提交了个java小写的标签， 需要内部转换为Java标签
            Map<String, Long> tagNameIdMap = tagDOList.stream().collect(Collectors.toMap(tagDo -> tagDo.getName().toLowerCase(), TagDO::getId));

            // 使用迭代器进行安全删除操作
            Iterator<String> iterator = notExistTags.iterator();
            while (iterator.hasNext()) {
                String notExistTag = iterator.next();
                // 转小写， 如果Map中相同的key，则表示该标签重复标签
                if (tagNameIdMap.containsKey(notExistTag.toLowerCase())) {
                    // 从不存在的标签集中清除在表中已存在的标签
                    iterator.remove();
                    // 并将对应ID添加到已存在的标签集合中
                    existedTags.add(String.valueOf(tagNameIdMap.get(notExistTag.toLowerCase())));
                }
            }
        }

        // 将提交上来的已存在表中的标签，文章标签关联关系入库
        if (!CollectionUtils.isEmpty(existedTags)) {
            List<ArticleTagRelDO> articleTagRelDOS = Lists.newArrayList();
            existedTags.forEach(tagId -> {
                ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder().articleId(articleId).tagId(Long.valueOf(tagId)).build();

                articleTagRelDOS.add(articleTagRelDO);
            });

            // 批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
        }

        // 将提交上来的标签，不存在表中的，入库保存
        if (!CollectionUtils.isEmpty(notExistTags)) {
            // 需要先将标签入库， 拿到对应标签ID， 再把文章-标签关联关系入库
            List<ArticleTagRelDO> articleTagRelDOS = Lists.newArrayList();
            notExistTags.forEach(tagName -> {
                TagDO tagDO = TagDO.builder().name(tagName).createTime(LocalDateTime.now()).updateTime(LocalDateTime.now()).build();
                tagMapper.insert(tagDO);

                // 获取保存的标签ID
                Long tagId = tagDO.getId();

                // 文章-标签关联关系
                ArticleTagRelDO articleTagRelDO = ArticleTagRelDO.builder().articleId(articleId).tagId(tagId).build();

                articleTagRelDOS.add(articleTagRelDO);
            });

            // 批量插入
            articleTagRelMapper.insertBatchSomeColumn(articleTagRelDOS);
        }

    }
}
